package com.my.netty.optimize.protocol;

import com.google.gson.*;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Type;
import java.nio.charset.StandardCharsets;

/**
 * 定义序列化接口
 */
public interface Serializer {

    /**
     * 反序列化方法
     *
     * @param clazz 反序列化成的java类型，json序列化需要
     * @param bytes 待反序列化的字节数组
     * @param <T>   反序列化成的java类型泛型
     * @return 反序列化结果
     */
    <T> T deserialize(Class<T> clazz, byte[] bytes);

    /**
     * 序列化方法
     *
     * @param obj 待反序列化的对象
     * @param <T> 待反序列化对象泛型
     * @return 序列化结果
     */
    <T> byte[] serialize(T obj);

    /**
     * 定义序列化算法枚举
     */
    enum Algorithm implements Serializer {
        //JDK序列化
        JAVA {
            @Override
            public <T> T deserialize(Class<T> clazz, byte[] bytes) {
                try (ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
                     ObjectInputStream ois = new ObjectInputStream(bis)) {
                    return (T) ois.readObject();
                } catch (Exception e) {
                    throw new RuntimeException("反序列化失败");
                }
            }

            @Override
            public <T> byte[] serialize(T obj) {
                try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
                     ObjectOutputStream oos = new ObjectOutputStream(bos)) {
                    oos.writeObject(obj);
                    return bos.toByteArray();
                } catch (Exception e) {
                    throw new RuntimeException("序列化失败");
                }
            }
        },
        //JSON序列化，gson不支持序列化Class对象，我们需要自己写一个ClassCodec注册到GsonBuilder中
        JSON {
            @Override
            public <T> T deserialize(Class<T> clazz, byte[] bytes) {
                String json = new String(bytes, StandardCharsets.UTF_8);
                return new GsonBuilder().registerTypeAdapter(Class.class, new ClassCodec()).create().fromJson(json, clazz);
            }

            @Override
            public <T> byte[] serialize(T obj) {
                String json = new GsonBuilder().registerTypeAdapter(Class.class, new ClassCodec()).create().toJson(obj);
                return json.getBytes(StandardCharsets.UTF_8);
            }
        },

        ;

        private static class ClassCodec implements JsonSerializer<Class<?>>, JsonDeserializer<Class<?>> {

            @Override
            public Class<?> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
                try {
                    String className = json.getAsString();
                    return Class.forName(className);
                } catch (ClassNotFoundException e) {
                    throw new JsonParseException(e);
                }
            }

            @Override
            public JsonElement serialize(Class<?> src, Type typeOfSrc, JsonSerializationContext context) {
                //class -> json
                return new JsonPrimitive(src.getName());
            }
        }

    }
}
